/* for clock_ValueRough */
#include <sys/time.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <netinet/ether.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <sys/uio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <unistd.h>
#include "rs_config.h"
#include "scps.h"
#include "scps_defines.h"
#include "scpstp.h"
#include "tap.h"
#include "ll.h"


#define DIVERT_PORT 52000


extern GW_ifs gw_ifs;

struct gwio *sock_interface = NULL;
struct gwio *divert_gwio = NULL;


#undef RCV_DEBUG

int scps_udp_port = SCPS_UDP_PORT;
int scps_udp_port1 = Other_SCPS_UDP_PORT;

void ll_set_udp_port(uint16_t port)
{
  scps_udp_port = port;
}

void ll_set_udp_port1(uint16_t port)
{
  scps_udp_port1 = port;
}

void ll_show_interface(struct gwio *intf)
{
  printf (" incoming empty  %d\n", TAILQ_EMPTY(&intf->incoming));
  printf (" outgoing empty  %d\n", TAILQ_EMPTY(&intf->outgoing));
  printf (" available empty %d\n", TAILQ_EMPTY(&intf->available));
  printf (" MTU           %d\n", intf->MTU);
  printf (" address       0x%x\n", intf->address);
  printf (" next_hop      0x%x\n", intf->next_hop);
  printf (" tp_socket    %d\n", intf->tp_socket);

  printf (" np_socket    %d\n", intf->np_socket);  
  printf (" et_socket    %d\n", intf->et_socket);  

  printf (" tap_a_fd     %d\n", intf->tap_a_fd);  
  printf (" tap_b_fd     %d\n", intf->tap_b_fd);  


  printf (" div_port     %d\n", intf->div_port);  
  printf (" div_a_port   %d\n", intf->div_a_port);  
  printf (" div_b_port   %d\n", intf->div_b_port);  
  printf (" is_free      %d\n", intf->is_free);  
  printf (" service_now  %d\n", intf->service_now);  
  printf (" overhead     %d\n", intf->overhead);  
  printf (" mss_ff       %d\n\n", intf->mss_ff);  
}

void ll_show(void)
{
  struct gwio *lookup;

  printf ("sock interface:%s\n", sock_interface != NULL ? "yes" : "no");

  for (lookup = sock_interface; lookup != NULL; lookup=lookup->next) {
    ll_show_interface(lookup);
  }

  if ( divert_gwio != NULL) {
    printf ("divert gwio:\n");
    ll_show_interface(divert_gwio);
  }
}


void create_divert_interface (uint32_t local_addr, int32_t port_number1)
{
  struct gwio *gwio, *index;
  int32_t port_number2;
  int32_t port_number3;

  local_addr = 0;

  port_number1 = gw_ifs.c_divport;
  port_number2 = gw_ifs.a.divport;
  port_number3 = gw_ifs.b.divport;

  if (!(local_addr))
    get_local_internet_addr ((char *) &local_addr);

  if (!(port_number1))
    port_number1 = DEF_C_PORT;

  if (!(port_number2))
    port_number2 = DEF_A_PORT;

  if (!(port_number3))
    port_number3 = DEF_B_PORT;

  gwio = (struct gwio *) malloc (sizeof (struct gwio));
  memset (gwio, 0, sizeof (struct gwio));
  gwio_init (gwio, local_addr);
  index = gwio;
  divert_gwio = gwio;

  gwio->tap_a_fd = tap_open (gw_ifs.a.tap_name) ;
  FD_SET (gwio->tap_a_fd, &llfd_set);
  if (gwio->tap_a_fd > ll_max_socket){
    ll_max_socket = gwio->tap_a_fd;
  }
  gwio->div_a_port = port_number2;
  gwio->tap_b_fd = tap_open (gw_ifs.b.tap_name);
  FD_SET (gwio->tap_b_fd, &llfd_set);
  if (gwio->tap_b_fd > ll_max_socket){
      ll_max_socket = gwio->tap_b_fd;
  }
  gwio->div_b_port = port_number3;

  if (!(scheduler.gwio))
    scheduler.gwio = (void *) gwio;
  else {
    index =  (struct gwio *) (scheduler.gwio);
    while (index->next)
    index = index->next;
    index->next = gwio;
    gwio ->next = (struct gwio *) (scheduler.gwio);
  }
  return;
}

int ll_iovsend (struct gwio *gwio, struct addrs addr,
      int protocol, int data_len, struct msghdr *my_msg,
      route *a_route, scps_np_rqts *rqts)
{
  int cc = 0;
  uint32_t remote_internet_addr;

  if (addr.nl_protocol == NL_PROTOCOL_IPV4) {
    remote_internet_addr = addr.nl_head.ipv4_addr;
    cc = ll_tap_send  (gwio, remote_internet_addr,
            protocol, data_len, my_msg, a_route, rqts);
  }
  return (cc);
}

/*
 *  ll_nbreceive performs a non-blocking receive 
 */

int ll_nbreceive (struct gwio *gwio, struct llbuf **buffer, int max_len, int *offset)
{
  volatile int cc;

  gwio->is_free = 0;
  if (!TAILQ_EMPTY(&gwio->incoming)) {
    struct llbuf *llbuf = TAILQ_FIRST(&gwio->incoming);
    TAILQ_REMOVE(&gwio->incoming, llbuf, entry);
    scheduler.gwio_data--;
    *offset = llbuf->offset;
    *buffer = llbuf;
    cc = llbuf->size;      
  } else
    cc = 0;
  gwio->is_free = 1;
  return (cc);
}

struct llbuf *alloc_llbuff (struct gwio *gwio)
{
  struct llbuf *buffer = NULL;

  gwio->is_free = 0;
  /* sigprocmask(SIG_BLOCK, &alarmset, 0x0); */

  if (!TAILQ_EMPTY(&gwio->available)) {
      buffer = TAILQ_FIRST(&gwio->available);
      TAILQ_REMOVE(&gwio->available, buffer, entry);
  }
  gwio->is_free = 1;
  /* sigprocmask(SIG_UNBLOCK, &alarmset, 0x0); */
  return (buffer);
}

void free_llbuf (struct gwio *gwio, struct llbuf *buffer)
{
  gwio->is_free = 0;
  /* sigprocmask(SIG_BLOCK, &alarmset, 0x0); */
  TAILQ_INSERT_TAIL(&gwio->available, buffer, entry);
  gwio->is_free = 1;
  /* sigprocmask(SIG_UNBLOCK, &alarmset, 0x0); */
}

void gwio_init (struct gwio *gwio, uint32_t local_addr)
{
  struct llbuf *llbuf;

  TAILQ_INIT(&gwio->incoming);
  TAILQ_INIT(&gwio->outgoing);
  TAILQ_INIT(&gwio->available);

  gwio->tp_socket = 0;
  gwio->next = NULL;
  gwio->is_free = 1;
  gwio->service_now = 0;
  gwio->address = local_addr;
  gwio->MTU = MAX_MTU;  /* A magic number (sorry), that assumes Ethernet */
  int i;
  for (i = 0; i < MAX_LL_QUEUE_ELEMENTS; i++) {
    llbuf = (struct llbuf *) malloc (sizeof (struct llbuf));
    if (llbuf != NULL) {
      memset (llbuf, 0, sizeof (struct llbuf));
      TAILQ_INSERT_TAIL(&gwio->available, llbuf, entry);
    } else
      break;
  }
}


struct llbuf *gwio_rx(struct gwio *gwio, int fd)
{
  if (!gwio->is_free || TAILQ_EMPTY(&gwio->available))
    return NULL;
  
  gwio->is_free = 0;
  
  struct llbuf *llbuf = TAILQ_FIRST(&gwio->available);
  llbuf->size = read (fd, (void *) &(llbuf->data), MAX_LL_DATA);
  if (llbuf->size > 0) {
    TAILQ_REMOVE(&gwio->available, llbuf, entry);

    if (fd == gwio->tap_a_fd) {
      llbuf->divert_port_number = gwio->div_a_port;
    } else if (fd == gwio->tap_b_fd) {
      llbuf->divert_port_number = gwio->div_b_port;
    } 
    llbuf->offset = 0;
    struct ethhdr *ethhdr = (struct ethhdr *)&llbuf->data[0];
    memcpy (llbuf->dst_mac, ethhdr->h_dest,  ETH_ALEN);
    memcpy (llbuf->src_mac, ethhdr->h_source, ETH_ALEN);

    llbuf->frame_type = ntohs(ethhdr->h_proto);
    llbuf->frame_size = llbuf->size;
    TAILQ_INSERT_TAIL(&gwio->incoming, llbuf, entry);
    scheduler.gwio_data++;
  }
  gwio->is_free = 1;
  return llbuf;
}

void service_interface (struct gwio *gwio)
{
  int to_read = 0, ready = 0;
  fd_set local_fd_set;


  if (!gwio->is_free || TAILQ_EMPTY(&gwio->available))
    return;

  memcpy (&local_fd_set, &llfd_set, sizeof (fd_set));

  struct timeval timeout = {.tv_sec = 0, .tv_usec = 0,};

  if ((select ((ll_max_socket + 1), &local_fd_set, 0x0, 0x0, &timeout)) < 1)
    return;

  gwio->is_free = 0;


  if (FD_ISSET (gwio->tap_a_fd, &local_fd_set)) {
      ready = gwio->tap_a_fd;
    }

  if (FD_ISSET (gwio->tap_b_fd, &local_fd_set)) {
      ready = gwio->tap_b_fd;
    }

  to_read = (ready & 0xFF);
  ready = (ready >> 8);


  /*  while ((handled--) && ((ready) || (to_read))) */
  while ((ready) || (to_read)) {
    struct llbuf *llbuf = TAILQ_FIRST(&gwio->available);
      if (llbuf != NULL)
        llbuf->size = read (to_read, (void *) &(llbuf->data), MAX_LL_DATA);
      if (llbuf == NULL || llbuf->size <= 0) {
        to_read = (ready & 0xFF);
        ready = (ready >> 8);
        continue;
      }
      TAILQ_REMOVE(&gwio->available, llbuf, entry);

      if (to_read == gwio->tap_a_fd) {
        llbuf->divert_port_number = gwio->div_a_port;
      } else if (to_read == gwio->tap_b_fd) {
        llbuf->divert_port_number = gwio->div_b_port;
      } 
      llbuf->offset = 0;
      struct ethhdr *ethhdr = (struct ethhdr *)&llbuf->data[0];
      memcpy (llbuf->dst_mac, ethhdr->h_dest,  ETH_ALEN);
      memcpy (llbuf->src_mac, ethhdr->h_source, ETH_ALEN);

      llbuf->frame_type = ntohs(ethhdr->h_proto);
      llbuf->frame_size = llbuf->size;
      TAILQ_INSERT_TAIL(&gwio->incoming, llbuf, entry);
      scheduler.gwio_data++;
  }
  gwio->is_free = 1;
}




